package jmh;

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.concurrent.TimeUnit;

/**
 * JMH 是一个面向 Java 语言或者其他 Java 虚拟机语言的性能基准测试框架
 * 1.@BenchmarkMode(Mode.All)
 * Mode有：
 * - Throughput: 整体吞吐量，例如“1秒内可以执行多少次调用” (thrpt,参加第5点)
 * - AverageTime: 调用的平均时间，例如“每次调用平均耗时xxx毫秒”。（avgt）
 * - SampleTime: 随机取样，最后输出取样结果的分布，例如“99%的调用在xxx毫秒以内，99.99%的调用在xxx毫秒以内”（simple）
 * - SingleShotTime: 以上模式都是默认一次 iteration 是 1s，唯有 SingleShotTime 是只运行一次。往往同时把 warmup 次数设为0，用于测试冷启动时的性能。（ss）
 * 2.@OutputTimeUnit(TimeUnit.MILLISECONDS)
 * 统计单位， 微秒、毫秒 、分、小时、天
 * 3.@State
 * 可参：JMHFirstBenchmark.java
 * 类注解，JMH测试类必须使用@State注解，State定义了一个类实例的生命周期，可以类比Spring Bean的Scope。由于JMH允许多线程同时执行测试，不同的选项含义如下：
 *
 * ```
 * Scope.Thread：默认的State，每个测试线程分配一个实例；
 * Scope.Benchmark：所有测试线程共享一个实例，用于测试有状态实例在多线程共享下的性能；
 * Scope.Group：每个线程组共享一个实例；
 * 4.@Warmup(iterations = 3, time = 1)
 * 预热（3次，每次时间为1）
 * 5.Measurement(iterations = 5, time = 5)
 * 正式测试（5次，每次时间5）
 * 6.@Threads(4)
 * 每个fork进程使用多少条线程去执行你的测试方法，默认值是Runtime.getRuntime().availableProcessors()。
 * 7.@fork(1)
 * JVM因为使用了profile-guided optimization而“臭名昭著”，这对于微基准测试来说十分不友好，因为不同测试方法的profile混杂在一起，“互相伤害”彼此的测试结果。
 * 对于每个@Benchmark方法使用一个独立的进程可以解决这个问题，这也是JMH的默认选项。注意不要设置为0，设置为n则会启动n个进程执行测试（似乎也没有太大意义）。
 * fork选项也可以通过方法注解以及启动参数来设置。
 *
 * @author qingyan 2022-03-09
 */
@BenchmarkMode(Mode.Throughput)
@Warmup(iterations = 3, time = 1)
@Measurement(iterations = 5, time = 5)
@Threads(4)
@Fork(1)
@State(value = Scope.Benchmark)
@OutputTimeUnit(TimeUnit.SECONDS)
public class JMHTest {

    /**
     * 1.@Benchmark
     * 很重要的方法注解，表示该方法是需要进行 benchmark 的对象。和@test 注解一致
     * 2.@Setup
     * 方法注解，会在执行 benchmark 之前被执行，正如其名，主要用于初始化。
     * 3.@TearDown (Level)
     *  方法注解，与@Setup 相对的，会在所有 benchmark 执行结束以后执行，主要用于资源的回收等。
     *    (Level)   用于控制 @Setup，@TearDown 的调用时机，默认是 Level.Trial。
     *
     *           Trial：每个benchmark方法前后；
     *           Iteration：每个benchmark方法每次迭代前后；
     *           Invocation：每个benchmark方法每次调用前后，谨慎使用，需留意javadoc注释；
     * 4.@Param
     * 可以用来指定某项参数的多种情况。特别适合用来测试一个函数在不同的参数输入的情况下的性能。
     *
     *
     *
     *
     */

    @Setup()
    @Benchmark
    public void test() {
        System.out.println(Thread.currentThread().getName());
    }

    public static void main(String[] args) throws RunnerException {
        Options options = new OptionsBuilder()
                .include(JMHTest.class.getSimpleName())
                .result("result.json")
                .build();
        new Runner(options).run();
    }
}

